home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
filesyst
/
dosfs
/
dmsdosfs.000
/
dmsdosfs
/
dmsdosfs-0.6.9b
/
dmsdos_spc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-29
|
31KB
|
1,069 lines
/*
linux/fs/dmsdos/dmsdos_spc.c
DMSDOS filesystem: special routines.
******************************************************************************
DMSDOS (Doublespace/Drivespace compressed MSDOS filesystem) for Linux
written 1995,1996 by Frank Gockel
(C) Copyright 1995,1996 by Frank Gockel
Some code of the dmsdos filesystem has been copied from the msdos filesystem
so there are the following additional copyrights:
(C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
(C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
(C) Copyright 1992-1995 by Linus Torvalds
The DMSDOS filesystem was inspired by the THS filesystem (a simple doublespace
DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
The DMSDOS filesystem is distributed under the Gnu General Public Licence.
See file COPYING for details.
******************************************************************************
*/
#include <linux/sched.h>
#include <linux/ctype.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/locks.h>
#include <asm/segment.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/msdos_fs.h>
#include <linux/dmsdos_fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/shm.h>
#include <linux/mman.h>
#include <asm/system.h>
/* hmmm... seems to have moved in newer kernels... */
#ifdef __FOR_KERNEL_1_3_8x
#include "../fat/msbuffer.h"
#else
#include "../msdos/msbuffer.h"
#endif
#define PRINTK(X)
#define CLPOS 26
/* change the 'define' to 'undef' if you want to inhibit write access */
#define DBL_WRITEACCESS
/* change this if your FAT bit size is not recognized correctly -- you
should also report this to the author */
#undef BELIEVE_SUPERBLOCK
int dbl_cvf_inos[MAXDBLFILES];
int dbl_cvf_startcluster[MAXDBLFILES];
Dblsb dblsb[MAXDBLFILES];
Acache mdfat[MDFATCACHESIZE];
Acache dfat[DFATCACHESIZE];
Acache bitfat[BITFATCACHESIZE];
int dbl_sb_dev[MAXDBLFILES];
int lowest_never_used_cvfnr=0;
struct buffer_head* read_real_sector(struct super_block* sb,int sector)
{ struct buffer_head*bh;
/*printk("DMSDOS: read_real_sector %d\n",sector);*/
#ifdef __FOR_KERNEL_1_3_8x
bh=fat_bread(sb,sector);
#else
bh=msdos_bread(sb,sector);
#endif
if(bh==NULL)printk("DMSDOS: read_real_sector %d failed\n",sector);
return bh;
}
struct buffer_head* noread_real_sector(struct super_block* sb,int sector)
{ struct buffer_head*bh;
/*printk("DMSDOS: noread_real_sector %d\n",sector);*/
#ifdef __FOR_KERNEL_1_3_8x
bh=fat_getblk(sb,sector);
if(bh==NULL)printk("DMSDOS: noread_real_sector %d failed\n",sector);
else fat_set_uptodate(sb,bh,1);
#else
bh=msdos_getblk(sb,sector);
if(bh==NULL)printk("DMSDOS: noread_real_sector %d failed\n",sector);
else msdos_set_uptodate(sb,bh,1);
#endif
return bh;
}
void bh_free(struct super_block* sb,struct buffer_head*bh)
{
#ifdef __FOR_KERNEL_1_3_8x
fat_brelse(sb,bh);
#else
msdos_brelse(sb,bh);
#endif
}
void bh_dirty(struct super_block*sb,struct buffer_head*bh)
{
#ifdef DBL_WRITEACCESS
#ifdef __FOR_KERNEL_1_3_8x
fat_mark_buffer_dirty(sb,bh,1);
#else
msdos_mark_buffer_dirty(sb,bh,1); /* !!! */
#endif
#else
printk("DMSDOS: illegal write access, ignored\n");
#endif
}
int calc_real_sector(int dblsector, int cvfnr)
{ int i;
int sz;
/* calculate physical sector */
i=0;sz=dblsector;
do
{ sz-=dblsb[cvfnr].s_anzahl[i];
++i;
}
while(sz>=0&&i<MAXFRAGMENT);
--i;
sz+=dblsb[cvfnr].s_anzahl[i]+dblsb[cvfnr].s_first[i];
return sz;
}
int dos_cluster2sector(struct super_block * sb,int clusternr)
{ return (clusternr-2)*MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start;
}
struct buffer_head* read_dbl_sector(struct super_block*sb,int dblsector,
int cvfnr)
{ int sector;
/*printk("DMSDOS: read_dbl_sector: sector=%d of CVF %d\n",dblsector,cvfnr+1);*/
sector=calc_real_sector(dblsector,cvfnr);
return read_real_sector(sb,sector);
}
struct buffer_head* noread_dbl_sector(struct super_block*sb,int dblsector,
int cvfnr)
{ int sector;
/*printk("DMSDOS: noread_dbl_sector: sector=%d of CVF %d\n",dblsector,cvfnr+1);
*/
sector=calc_real_sector(dblsector,cvfnr);
return noread_real_sector(sb,sector);
}
int setup_fragment(struct super_block*sb, int cvfnr)
{ int fragmentzaehler;
int clusterzaehler;
int akt_cluster;
int folge_cluster;
int i;
/*printk("DMSDOS: setup_fragment CVF %d\n",cvfnr+1);*/
fragmentzaehler=0;
folge_cluster=dbl_cvf_startcluster[cvfnr];
do
{
clusterzaehler=0;
dblsb[cvfnr].s_first[fragmentzaehler]=folge_cluster;
do
{ akt_cluster=folge_cluster;
folge_cluster=fat_access(sb,akt_cluster,-1);
++clusterzaehler;
}
while(folge_cluster==akt_cluster+1);
dblsb[cvfnr].s_anzahl[fragmentzaehler]=clusterzaehler;
/*printk("DMSDOS: firstclust=%d anz=%d\n",
dblsb[cvfnr].s_first[fragmentzaehler],
dblsb[cvfnr].s_anzahl[fragmentzaehler]);
*/
++fragmentzaehler;
}
while(folge_cluster>0&&fragmentzaehler<MAXFRAGMENT);
if(fragmentzaehler==MAXFRAGMENT&&folge_cluster>0)
{ /* zu fragmentiert, raus */
dbl_cvf_inos[cvfnr]=0;
dbl_sb_dev[cvfnr]=0;
printk("DMSDOS: CVF %d too fragmented, not mounted. See doc.\n",cvfnr+1);
return -1;
}
/*printk("DMSDOS: CVF %d has %d fragment(s)\n",cvfnr+1,fragmentzaehler);*/
/* convert cluster-oriented numbers into sector-oriented ones */
for(i=0;i<fragmentzaehler;++i)
{ /*printk("DMSDOS: umrechnen 1\n");*/
dblsb[cvfnr].s_first[i]=dos_cluster2sector(sb,dblsb[cvfnr].s_first[i]);
/*printk("DMSDOS: umrechnen 2\n");*/
dblsb[cvfnr].s_anzahl[i]*=MSDOS_SB(sb)->cluster_size;
/*printk("DMSDOS: umrechnen 3\n");*/
}
return 0;
}
void setup_mdfat(struct super_block*sb,int cvfnr,int nofix)
{
struct buffer_head*bh;
struct buffer_head*bh2;
int i,mdfatb,fatb;
unsigned char * pp;
/*printk("DMSDOS: setup_mdfat with CVF %d\n",cvfnr+1);*/
for(i=0;i<MAXFRAGMENT;++i)
{ dblsb[cvfnr].s_first[i]=0;
dblsb[cvfnr].s_anzahl[i]=0;
}
if(setup_fragment(sb,cvfnr)<0)return;
/*printk("DMSDOS: reading %d. CVF boot block...\n",cvfnr+1);*/
bh=read_dbl_sector(sb,0,cvfnr);
if(bh==NULL)
{ printk("DMSDOS: unable to read super block of CVF %d\n",cvfnr+1);
dbl_cvf_inos[cvfnr]=0;
dbl_sb_dev[cvfnr]=0;
return;
}
/*printk("DMSDOS: boot block read finished\n");*/
pp=&(bh->b_data[3]);
if(strncmp(pp,"MSDBL6.0",8)!=0&&strncmp(pp,"MSDSP6.0",8)!=0)
{ printk("DMSDOS: MSDBL/MSDSP signature not found, CVF %d skipped\n",cvfnr+1);
dbl_cvf_inos[cvfnr]=0;
dbl_sb_dev[cvfnr]=0;
bh_free(sb,bh);
return;
}
pp=&(bh->b_data[45]);
dblsb[cvfnr].s_dcluster=CHS(pp);
if(dblsb[cvfnr].s_dcluster&0x8000)dblsb[cvfnr].s_dcluster|=0xffff0000;
pp=&(bh->b_data[36]);
dblsb[cvfnr].s_mdfatstart=CHS(pp)+1;
pp=&(bh->b_data[17]);
dblsb[cvfnr].s_rootdiranzentry=CHS(pp);
dblsb[cvfnr].s_sectperclust=((unsigned long)(bh->b_data[13]));
pp=&(bh->b_data[39]);i=CHS(pp);/*i=res0*/
dblsb[cvfnr].s_bootblock=i;
pp=&(bh->b_data[14]);
dblsb[cvfnr].s_fatstart=i+CHS(pp);
pp=&(bh->b_data[41]);
dblsb[cvfnr].s_rootdir=i+CHS(pp);
pp=&(bh->b_data[43]);
dblsb[cvfnr].s_datastart=i+CHS(pp)+2;
pp=&(bh->b_data[57]);
#ifdef BELIEVE_SUPERBLOCK
if(CHL(pp)==0x20203631)dblsb[cvfnr].s_16bitfat=1;
else if(CHL(pp)==0x20203231)dblsb[cvfnr].s_16bitfat=0;
else
{ /* analyse emulated boot block (new format compatibility [arghhh...] ) */
printk("DMSDOS: CVF %d seems to be in new format, analysing emulated boot block\n",
cvfnr+1);
/* win95 has long filenames */
if((dblsb[cvfnr].s_support_lfn&3)==2)dblsb[cvfnr].s_support_lfn|=1;
#endif
bh2=read_dbl_sector(sb,dblsb[cvfnr].s_bootblock,cvfnr);
if(bh2==NULL)
{ printk("DMSDOS: unable to read emulated boot block of CVF %d\n",cvfnr+1);
dbl_cvf_inos[cvfnr]=0;
dbl_sb_dev[cvfnr]=0;
bh_free(sb,bh);
return;
}
pp=&(bh2->b_data[57]);
if(CHL(pp)==0x20203631)dblsb[cvfnr].s_16bitfat=1;
else if(CHL(pp)==0x20203231)dblsb[cvfnr].s_16bitfat=0;
else
{
pp=&(bh->b_data[62]);
dblsb[cvfnr].s_16bitfat=(CHS(pp)>32) ? 1 : 0;
printk("DMSDOS: FAT bit size of CVF %d not recognized, guessed %d bit\n",
cvfnr+1,CHS(pp)>32 ? 16 : 12 );
}
bh_free(sb,bh2);
#ifdef BELIEVE_SUPERBLOCK
}
#endif
dblsb[cvfnr].s_full=0;
/* calculate maximum cluster nr (fixes lost cluster messages) */
mdfatb=(dblsb[cvfnr].s_bootblock-dblsb[cvfnr].s_mdfatstart);
mdfatb*=((dblsb[cvfnr].s_sectperclust>16)?102:128);
mdfatb-=dblsb[cvfnr].s_dcluster;
fatb=512*(dblsb[cvfnr].s_rootdir-dblsb[cvfnr].s_fatstart);
if(dblsb[cvfnr].s_16bitfat)fatb/=2; else fatb=(2*fatb)/3;
dblsb[cvfnr].s_max_cluster=((mdfatb<fatb)?mdfatb:fatb)-1;
if(dblsb[cvfnr].s_16bitfat)
{ if(dblsb[cvfnr].s_max_cluster>0xFFF6)dblsb[cvfnr].s_max_cluster=0xFFF6;
}
else
{ if(dblsb[cvfnr].s_max_cluster>0xFF6)dblsb[cvfnr].s_max_cluster=0xFF6;
}
/* adapt max_cluster according to dos' limits */
dblsb[cvfnr].s_max_cluster2=dblsb[cvfnr].s_max_cluster;
pp=&(bh->b_data[32]);
i=CHL(pp);
pp=&(bh->b_data[22]);
i-=CHS(pp);
pp=&(bh->b_data[14]);
i-=CHS(pp);
i-=dblsb[cvfnr].s_rootdiranzentry>>4;
i=(i>>4)+1;
if(i<=dblsb[cvfnr].s_max_cluster)
{ dblsb[cvfnr].s_max_cluster=i;
}
else
{ printk("DMSDOS: CVF %d: dos max_cluster=%d too large, cutting to %d.\n",
cvfnr+1,i,dblsb[cvfnr].s_max_cluster);
}
bh_free(sb,bh);
/*
printk("DMSDOS: dcluster=%d\n",dblsb[cvfnr].s_dcluster);
printk("DMSDOS: mdfatstart=%d\n",dblsb[cvfnr].s_mdfatstart);
printk("DMSDOS: rootdiranzentry=%d\n",dblsb[cvfnr].s_rootdiranzentry);
printk("DMSDOS: sectperclust=%d\n",dblsb[cvfnr].s_sectperclust);
printk("DMSDOS: fatstart=%d\n",dblsb[cvfnr].s_fatstart);
printk("DMSDOS: rootdir=%d\n",dblsb[cvfnr].s_rootdir);
printk("DMSDOS: %d bit FAT\n",dblsb[cvfnr].s_16bitfat ? 16 : 12);
*/
/* error test */
i=simple_check(sb,cvfnr);
if(i==-1||i==-2)
{ printk("DMSDOS: CVF %d has serious errors, setting to read-only.\n",
cvfnr+1);
dblsb[cvfnr].s_comp=READ_ONLY;
}
if(i==-3)
{ printk("DMSDOS: CVF %d has minor errors, errors ignored.\n",cvfnr+1);
}
if(dblsb[cvfnr].s_sectperclust>16)
{
printk("DMSDOS: CVF %d is in drivespace 3 format.\n",cvfnr+1);
if(dblsb[cvfnr].s_comp!=READ_ONLY&&nofix==0)fixdir(sb,cvfnr);
}
}
struct wait_queue * mdfatwait=NULL;
int mdfatlock=0;
void lock_mdfat(void)
{ while(mdfatlock)sleep_on(&mdfatwait);
mdfatlock=1;
}
void unlock_mdfat(void)
{ mdfatlock=0;
wake_up(&mdfatwait);
}
struct wait_queue * dfatwait=NULL;
int dfatlock=0;
void lock_dfat(void)
{ while(dfatlock)sleep_on(&dfatwait);
dfatlock=1;
}
void unlock_dfat(void)
{ dfatlock=0;
wake_up(&dfatwait);
}
struct wait_queue * bitfatwait=NULL;
int bitfatlock=0;
void lock_bitfat(void)
{ while(bitfatlock)sleep_on(&bitfatwait);
bitfatlock=1;
}
void unlock_bitfat(void)
{ bitfatlock=0;
wake_up(&bitfatwait);
}
void read_mdfat_sector(struct super_block*sb,int area,int merk_i,int cvfnr)
{
/*printk("DMSDOS: reading dbl_mdfat sector %d of CVF %d\n",area,cvfnr+1);*/
if(mdfat[merk_i].a_buffer!=NULL)bh_free(sb,mdfat[merk_i].a_buffer);
if((mdfat[merk_i].a_buffer=read_dbl_sector(sb,area,cvfnr))==NULL)
{ panic("DMSDOS: unable to read mdfat\n");
}
mdfat[merk_i].a_area=area;
mdfat[merk_i].a_time=CURRENT_TIME;
mdfat[merk_i].a_acc=0;
mdfat[merk_i].a_cvfnr=cvfnr;
}
void read_dfat_sector(struct super_block*sb,int area,int merk_i,int cvfnr)
{
/*printk("DMSDOS: reading dbl_fat sector %d of CVF %d\n",area,cvfnr+1);*/
if(dfat[merk_i].a_buffer!=NULL)bh_free(sb,dfat[merk_i].a_buffer);
if((dfat[merk_i].a_buffer=read_dbl_sector(sb,area,cvfnr))==NULL)
{ panic("DMSDOS: unable to read dfat\n");
}
dfat[merk_i].a_area=area;
dfat[merk_i].a_time=CURRENT_TIME;
dfat[merk_i].a_acc=0;
dfat[merk_i].a_cvfnr=cvfnr;
/*printk("cluster 2 fat entry=%d\n",mdfat[cvfnr].fat_data[merk_i][2]);*/
}
void read_bitfat_sector(struct super_block*sb,int area,int merk_i,int cvfnr)
{
/*printk("DMSDOS: reading bitfat sector %d of CVF %d\n",area,cvfnr+1);*/
if(bitfat[merk_i].a_buffer!=NULL)bh_free(sb,bitfat[merk_i].a_buffer);
if((bitfat[merk_i].a_buffer=read_dbl_sector(sb,area,cvfnr))==NULL)
{ panic("DMSDOS: unable to read bitfat\n");
}
bitfat[merk_i].a_area=area;
bitfat[merk_i].a_time=CURRENT_TIME;
bitfat[merk_i].a_acc=0;
bitfat[merk_i].a_cvfnr=cvfnr;
}
void u_dumpcache(Acache*c)
{ printk("area=%d time=%ld acc=%d cvfnr=%d buffer=%p\n",c->a_area,c->a_time,
c->a_acc,c->a_cvfnr+1,c->a_buffer);
}
void dumpcache(void)
{ int i;
printk("DMSDOS: mdfat cache:\n");
for(i=0;i<MDFATCACHESIZE;++i)u_dumpcache(&(mdfat[i]));
printk("DMSDOS: dfat cache:\n");
for(i=0;i<DFATCACHESIZE;++i)u_dumpcache(&(dfat[i]));
printk("DMSDOS: bitfat cache:\n");
for(i=0;i<BITFATCACHESIZE;++i)u_dumpcache(&(bitfat[i]));
}
void dbl_mdfat_value(struct super_block*sb,int clusternr,int cvfnr,
Mdfat_entry*new,
Mdfat_entry*mde)
{ int i;
int area;
int pos;
int offset;
unsigned long min_time;
unsigned int min_acc;
int merk_i,merk_i2,merk_i3;
unsigned char * pp;
int res;
unsigned char mdfat_raw_field[5];
if(dblsb[cvfnr].s_sectperclust>16)
/* the following formula is _guessed_ and may be awfully wrong
pos=(dblsb[cvfnr].s_dcluster+clusternr)*5+512*dblsb[cvfnr].s_mdfatstart;
... it *is* wrong... */
/* or this one... with sector alignment - changes in max_cluster
necessary!!!! */
pos=(dblsb[cvfnr].s_dcluster+clusternr)*5
+((dblsb[cvfnr].s_dcluster+clusternr)/102)*2
+512*dblsb[cvfnr].s_mdfatstart;
else
pos=(dblsb[cvfnr].s_dcluster+clusternr)*4+512*dblsb[cvfnr].s_mdfatstart;
area=pos/SECTOR_SIZE;
offset=(pos%SECTOR_SIZE);
lock_mdfat();
merk_i2=-1; /* never free this cached sector */
mdfat_redo:
min_time=mdfat[0].a_time;
min_acc=mdfat[0].a_acc;
merk_i=0;
if(merk_i2==0)
{ min_time=mdfat[1].a_time;
min_acc=mdfat[1].a_acc;
merk_i=1;
}
/* find area and cvfnr in cache */
for(i=0;i<MDFATCACHESIZE;++i)
{ if( ( mdfat[i].a_time<min_time||
(mdfat[i].a_time==min_time&&mdfat[i].a_acc<min_acc)
) &&merk_i2!=i)
{ min_time=mdfat[i].a_time;
min_acc=mdfat[i].a_acc;
merk_i=i;
}
if(mdfat[i].a_buffer!=NULL&&area==mdfat[i].a_area&&cvfnr==mdfat[i].a_cvfnr)
{ /* found */
if(mdfat[i].a_time==CURRENT_TIME)++mdfat[i].a_acc;
else
{ mdfat[i].a_time=CURRENT_TIME;
mdfat[i].a_acc=0;
}
merk_i=i;
goto mdfat_okay; /************* goto ************************/
}
}
/* merk_i = least recently used entry number */
read_mdfat_sector(sb,area,merk_i,cvfnr);
mdfat_okay:
/*
pp=&(mdfat[merk_i].a_buffer->b_data[offset]);
res=CHL(pp);
if(new)
{ pp[0]=*new;pp[1]=*new>>8;pp[2]=*new>>16;pp[3]=*new>>24;
bh_dirty(sb,mdfat[merk_i].a_buffer);
/printk("DMSDOS: mdfat write cluster=%d value=0x%08x\n",clusternr,*new);/
}
unlock_mdfat();
return res;
*/
if(dblsb[cvfnr].s_sectperclust<=16)
{ /* normal... */
pp=&(mdfat[merk_i].a_buffer->b_data[offset]);
res=CHL(pp);
mde->sector_minus_1=res&0x1fffff;
mde->unknown=(res&0x00200000)>>21;
mde->size_lo_minus_1=(res&0x03c00000)>>22;
mde->size_hi_minus_1=(res&0x3c000000)>>26;
mde->flags=((res&0xC0000000)>>30)&3;
if(new)
{ res=new->sector_minus_1;res&=0x1fffff;
/* unknown bit ??? don't know... set to zero */
res|=new->size_lo_minus_1<<22;res&=0x03ffffff;
res|=new->size_hi_minus_1<<26;res&=0x3fffffff;
res|=new->flags<<30;
pp[0]=res;pp[1]=res>>8;pp[2]=res>>16;pp[3]=res>>24;
bh_dirty(sb,mdfat[merk_i].a_buffer);
}
unlock_mdfat();
return;
}
/* drivspace 3 mdfat access _experimental_ but seems to work */
/* read 2. sector if necessary */
if(offset+4>511&&merk_i2==-1)
{ merk_i2=merk_i;
++area;
goto mdfat_redo;
}
if(offset+4>511)
{ merk_i3=merk_i;
merk_i=merk_i2;
merk_i2=merk_i3;
}
else merk_i2=merk_i;
/* copy data in mdfat_raw_field (5 byte) */
for(i=0;i<5;++i)
{ if(i+offset<512)
mdfat_raw_field[i]=mdfat[merk_i].a_buffer->b_data[i+offset];
else
mdfat_raw_field[i]=mdfat[merk_i2].a_buffer->b_data[i+offset-512];
}
/* setup mde */
mde->sector_minus_1=CHL(mdfat_raw_field)&0xffffff;
mde->unknown=(CHL(mdfat_raw_field)&0x3000000)>>24;
mde->size_lo_minus_1=(mdfat_raw_field[3]>>2)&0x3f;
mde->size_hi_minus_1=mdfat_raw_field[4]&0x3f;
mde->flags=(mdfat_raw_field[4]>>6)&3;
/* if new!=NULL, setup 5 byte from new in mdfat_raw_field */
if(new)
{ mdfat_raw_field[0]=new->sector_minus_1&0xffffff;
/* unknown bits set to zero */
mdfat_raw_field[1]=new->sector_minus_1>>8;
mdfat_raw_field[2]=new->sector_minus_1>>16;
mdfat_raw_field[3]=(new->sector_minus_1>>24)&3;
mdfat_raw_field[3]|=new->size_lo_minus_1<<2;
mdfat_raw_field[4]=new->size_hi_minus_1&0x3f;
mdfat_raw_field[4]|=new->flags<<6;
/* write back mdfat_raw_entry */
for(i=0;i<5;++i)
{ if(i+offset<512)
mdfat[merk_i].a_buffer->b_data[i+offset]=mdfat_raw_field[i];
else
mdfat[merk_i2].a_buffer->b_data[i+offset-512]=mdfat_raw_field[i];
}
/* mark buffer dirty (both if necessary) */
bh_dirty(sb,mdfat[merk_i].a_buffer);
if(merk_i!=merk_i2)bh_dirty(sb,mdfat[merk_i2].a_buffer);
}
unlock_mdfat();
return;
}
int dbl_mdfat_cluster2sector(struct super_block*sb,int clusternr,int cvfnr)
{ Mdfat_entry mde;
dbl_mdfat_value(sb,clusternr,cvfnr,NULL,&mde);
return mde.sector_minus_1+1;
}
int dbl_fat_nextcluster(struct super_block*sb,int clusternr,int cvfnr,int*new)
{ int i;
int area;
int pos;
int offset;
unsigned long min_time;
unsigned int min_acc;
int merk_i;
int res;
int newval;
int merk_i2;
int merk_i3;
int offset2;
merk_i2=-1; /* never free this cached sector */
pos= dblsb[cvfnr].s_fatstart*512;
pos+= dblsb[cvfnr].s_16bitfat ? clusternr*2 : clusternr*3/2;
area=pos/SECTOR_SIZE;
offset=pos%SECTOR_SIZE;
lock_dfat();
dfat_retry:
min_time=dfat[0].a_time;
min_acc=dfat[0].a_acc;
merk_i=0;
if(merk_i2==0)
{ min_time=dfat[1].a_time;
min_acc=dfat[1].a_acc;
merk_i=1;
}
/* find area and cvfnr in cache */
for(i=0;i<DFATCACHESIZE;++i)
{ if( ( dfat[i].a_time<min_time ||
(dfat[i].a_time==min_time&&dfat[i].a_acc<min_acc)
) &&merk_i2!=i)
{ min_time=dfat[i].a_time;
min_acc=dfat[i].a_acc;
merk_i=i;
}
if(dfat[i].a_buffer!=NULL&&area==dfat[i].a_area&&cvfnr==dfat[i].a_cvfnr)
{ /* found */
if(dfat[i].a_time==CURRENT_TIME)++dfat[i].a_acc;
else
{ dfat[i].a_time=CURRENT_TIME;
dfat[i].a_acc=0;
}
merk_i=i;
goto dfat_okay;
}
}
/* merk_i = least recently used entry */
read_dfat_sector(sb,area,merk_i,cvfnr);
dfat_okay:
if(offset==511&&merk_i2==-1)
{ /* ⁿbertrag, mist, folgesektor auch noch lesen, alten nicht rauswerfen */
merk_i2=merk_i;
++area;
goto dfat_retry;
}
if(offset==511)
{ /* gerade zweiten sektor gelesen */
merk_i3=merk_i;
merk_i=merk_i2; /* wiederherstellen */
merk_i2=merk_i3; /* folgesektor */
offset2=0;
}
else
{ /* normal */
merk_i2=merk_i;
offset2=offset+1;
}
/*
printk("DMSDOS FAT lookup: area=%d merk_i=%d merk_i2=%d offset=%d offset2=%d\n",
area,merk_i,merk_i2,offset,offset2);
printk("DMSDOS FAT lookup: low=%d high=%d\n",
dfat[merk_i].a_buffer->b_data[offset],
dfat[merk_i2].a_buffer->b_data[offset2]);
*/
res=dfat[merk_i].a_buffer->b_data[offset];
res&=0xff; /* grmpf... sign problems !!!! this is brutal but it works */
res|=dfat[merk_i2].a_buffer->b_data[offset2]<<8;
res&=0xffff;
if(new)
{ if(dblsb[cvfnr].s_16bitfat) newval=*new&0xFFFF;
else
{ if(clusternr&1) newval=(res&0xF) | ((*new&0xFFF)<<4);
else newval=(res&0xF000) | (*new&0xFFF);
}
dfat[merk_i].a_buffer->b_data[offset]=newval;
dfat[merk_i2].a_buffer->b_data[offset2]=newval>>8;
bh_dirty(sb,dfat[merk_i].a_buffer);
if(merk_i!=merk_i2)bh_dirty(sb,dfat[merk_i2].a_buffer);
}
unlock_dfat();
if(dblsb[cvfnr].s_16bitfat)return res>=0xFFF7 ? -1 : res;
if(clusternr&1)res>>=4; else res&=0xfff;
return res>=0xff7 ? -1 : res;
}
int dbl_bitfat_value(struct super_block*sb,int sektornr,int cvfnr,int*new)
{ int i;
int area;
int pos;
int offset;
unsigned long min_time;
unsigned int min_acc;
int merk_i;
unsigned char * pp;
int bitmask;
int res;
int newval;
if(sektornr<dblsb[cvfnr].s_datastart)return -1;
if(sektornr>dblsb[cvfnr].s_dataend)return -1;
pos=SECTOR_SIZE+((sektornr-dblsb[cvfnr].s_datastart)>>4)*2;
area=pos/SECTOR_SIZE;
offset=(pos%SECTOR_SIZE);
bitmask=0x8000>>((sektornr-dblsb[cvfnr].s_datastart)&15);
lock_bitfat();
min_time=bitfat[0].a_time;
min_acc=bitfat[0].a_acc;
merk_i=0;
/* find area and cvfnr in cache */
for(i=0;i<BITFATCACHESIZE;++i)
{ if(bitfat[i].a_time<min_time ||
(bitfat[i].a_time==min_time&&bitfat[i].a_acc<min_acc) )
{ min_time=bitfat[i].a_time;
min_acc=bitfat[i].a_acc;
merk_i=i;
}
if(bitfat[i].a_buffer!=NULL&&area==bitfat[i].a_area&&
cvfnr==bitfat[i].a_cvfnr)
{ /* found */
if(bitfat[i].a_time==CURRENT_TIME)++bitfat[i].a_acc;
else
{ bitfat[i].a_time=CURRENT_TIME;
bitfat[i].a_acc=0;
}
merk_i=i;
goto bitfat_okay; /************* goto ************************/
}
}
/* merk_i = least recently used entry number */
read_bitfat_sector(sb,area,merk_i,cvfnr);
bitfat_okay:
pp=&(bitfat[merk_i].a_buffer->b_data[offset]);
res=CHS(pp);
if(new)
{ newval= (*new) ? (res|bitmask) : (res&~bitmask);
pp[0]=newval;pp[1]=newval>>8;
bh_dirty(sb,bitfat[merk_i].a_buffer);
}
unlock_bitfat();
return (res&bitmask) ? 1 : 0;
}
int dbltest(struct inode* inode)
{ int i;
if(lowest_never_used_cvfnr==0)return 0; /* dmsdos was never in action,
values are undefined
(this is only for umsdos since
umsdos may call this without
dbl_init being called before) */
/* am I the inode of a CVF? */
if(inode->i_ino==0)return 0; /* there is no zero ino for dmsdos */
for(i=0;i<MAXDBLFILES;++i)
{ if(dbl_cvf_inos[i]==inode->i_ino&&dbl_sb_dev[i]==inode->i_sb->s_dev)
return (i+1)<<DMSDOS_CVFNR_BITS;
}
return 0;
}
int dostest(struct inode* inode)
{ /* am I a real DOS inode (=1) or an inode of or in a CVF (=0) */
if(inode->i_ino<=DMSDOS_ORIGINO_MASK&&dbltest(inode)==0)return 1;
return 0;
}
int find_free_cvfnr(void)
{ int i;
for(i=0;i<MAXDBLFILES;++i)
{ if(dbl_cvf_inos[i]==0)return i;
}
return -1;
}
int dbl_init(struct super_block* sb, void* datai, int comp,int cf,int ilong,
int vfat,int low,int nofix)
{ int i,j;
int cvfnr;
struct buffer_head * bh;
struct msdos_dir_entry * data;
char dname[]=" . ";
printk("DMSDOS filesystem version %d.%d.%d%s"
#ifndef DBL_WRITEACCESS
" read-only"
#else
" read-write"
#endif
#ifdef SERIALIZE_RW_ACCESS
" serialized"
#endif
"\n",
(DMSDOS_VERSION&0xff0000)>>16,
(DMSDOS_VERSION&0x00ff00)>>8,DMSDOS_VERSION&0xff,DMSDOS_LT);
if(lowest_never_used_cvfnr==0)
{ /* first call of DMSDOS filesystem, initialising variables */
/* clear rwlist */
clear_list();
for(i=0;i<MAXDBLFILES;++i)
{ dbl_cvf_inos[i]=0;
dbl_sb_dev[i]=0;
}
for(i=0;i<MDFATCACHESIZE;++i)
{ mdfat[i].a_time=0;
mdfat[i].a_acc=0;
mdfat[i].a_buffer=NULL;
}
for(i=0;i<DFATCACHESIZE;++i)
{ dfat[i].a_time=0;
dfat[i].a_acc=0;
dfat[i].a_buffer=NULL;
}
for(i=0;i<BITFATCACHESIZE;++i)
{ bitfat[i].a_time=0;
bitfat[i].a_acc=0;
bitfat[i].a_buffer=NULL;
}
lowest_never_used_cvfnr=MAXDBLFILES;
}
/* check for maximum cluster size to avoid fs corruption */
if(MSDOS_SB(sb)->cluster_size>DMSDOS_MAX_DOS_SECTPERCLUST)
{ printk("DMSDOS: msdos cluster size too large, can't continue\n");
return -1;
}
for(i=0;i<MSDOS_SB(sb)->dir_entries/MSDOS_DPS;++i)
{ bh=read_real_sector(sb,MSDOS_SB(sb)->dir_start+i);
if(bh==NULL)
{ printk("DMSDOS: unable to read msdos root directory\n");
return -1;
}
data=(struct msdos_dir_entry*) bh->b_data;
for(j=0;j<MSDOS_DPS;++j)
{ if(strncmp(data[j].name,"DRVSPACE",8)==0
|| strncmp(data[j].name,"DBLSPACE",8)==0 )
{ if(data[j].name[8]>='0'&&data[j].name[8]<='9'
&&data[j].name[9]>='0'&&data[j].name[9]<='9'
&&data[j].name[10]>='0'&&data[j].name[10]<='9'
){ /* it is a CVF */
if((cvfnr=find_free_cvfnr())>=0)
{ dbl_cvf_startcluster[cvfnr]=data[j].start;
dblsb[cvfnr].s_dataend=data[j].size/SECTOR_SIZE-2;
/* ^^^ the last
sector seems to be
reserved for some
purpose...
*/
#ifndef DBL_WRITEACCESS
comp=READ_ONLY;
#endif
dblsb[cvfnr].s_comp=comp;
dblsb[cvfnr].s_cfaktor=cf;
dblsb[cvfnr].s_support_lfn=ilong+4*vfat;
dbl_cvf_inos[cvfnr]=(MSDOS_SB(sb)->dir_start+i)*MSDOS_DPS+j;
dbl_sb_dev[cvfnr]=sb->s_dev;
strncpy(dname,data[j].name,8);
strncpy(&(dname[9]),&(data[j].name[8]),3);
printk("DMSDOS: mounting CVF %s as CVF %d on device %x ino=%d %s...\n",
dname,cvfnr+1,dbl_sb_dev[cvfnr],dbl_cvf_inos[cvfnr],
comp==READ_ONLY?"read-only":"read-write");
setup_mdfat(sb,cvfnr,nofix);
if(((dblsb[cvfnr].s_support_lfn&1)==0)||low!=0)
dblsb[cvfnr].s_support_lfn|=8; /*uppercase->lowercase on*/
}
else printk("DMSDOS: too many CVFs, ignoring\n");
}
}
}
bh_free(sb,bh);
}
/*printk("DMSDOS: msdos_read_super reports data_start=%d cluster_size=%d\n",
MSDOS_SB(sb)->data_start,MSDOS_SB(sb)->cluster_size);*/
return 0;
}
void exit_dbl(struct super_block*sb)
{ int cvfnr,j;
if(lowest_never_used_cvfnr==0)return;
for(cvfnr=0;cvfnr<MAXDBLFILES;++cvfnr)
{ if(dbl_sb_dev[cvfnr]==sb->s_dev)
{ /*printk("DMSDOS: CVF %d ino=%d on device %d unmounted.\n",
cvfnr+1,dbl_cvf_inos[cvfnr],sb->s_dev);*/
/* kill rwlist entries */
clear_list_cvfnr(cvfnr);
/* zero out special infos about unmounted cvf */
dbl_sb_dev[cvfnr]=0;
dbl_cvf_inos[cvfnr]=0;
/* kill buffers used by unmounted cvf */
for(j=0;j<MDFATCACHESIZE;++j)
{ if(mdfat[j].a_cvfnr==cvfnr)
{ if(mdfat[j].a_buffer!=NULL)
{ bh_free(sb,mdfat[j].a_buffer);
mdfat[j].a_buffer=NULL;
}
mdfat[j].a_time=0;
mdfat[j].a_acc=0;
}
}
for(j=0;j<DFATCACHESIZE;++j)
{ if(dfat[j].a_cvfnr==cvfnr)
{ if(dfat[j].a_buffer!=NULL)
{ bh_free(sb,dfat[j].a_buffer);
dfat[j].a_buffer=NULL;
}
dfat[j].a_time=0;
dfat[j].a_acc=0;
}
}
for(j=0;j<BITFATCACHESIZE;++j)
{ if(bitfat[j].a_cvfnr==cvfnr)
{ if(bitfat[j].a_buffer!=NULL)
{ bh_free(sb,bitfat[j].a_buffer);
bitfat[j].a_buffer=NULL;
}
bitfat[j].a_time=0;
bitfat[j].a_acc=0;
}
}
}
}
}
/* simple fs check routines (DON'T mount obviously damaged filesystems rw)
*/
int simple_check(struct super_block*sb,int cvfnr)
{ /* unsigned char field[512*1024]; grr... panics (stack overflow) */
unsigned char *field;
unsigned char bits[8]={1,2,4,8,16,32,64,128};
int i,j,val;
Mdfat_entry mde;
#define setbit(nr) field[nr/8]|=bits[nr%8]
#define getbit(nr) (field[nr/8]&bits[nr%8])
/* check fat */
/* get memory for field */
val=dblsb[cvfnr].s_max_cluster2/8 + 1;
field=(unsigned char*)MALLOC(val);
if(field==NULL)
{ printk("DMSDOS: simple_check aborted (no memory)\n");
return 1;
}
for(i=0;i<val;++i)field[i]=0;
for(i=2;i<=dblsb[cvfnr].s_max_cluster2;++i)
{ val=dbl_fat_nextcluster(sb,i,cvfnr,NULL);
if(val!=0&&val!=-1)
{
if(getbit(val))
{ printk("DMSDOS: FAT crosslink in CVF %d detected (cluster %d)\n",
cvfnr+1,i);
FREE(field);
return -1;
}
setbit(val);
}
}
FREE(field);
/* check mdfat */
val=dblsb[cvfnr].s_dataend/8 + 1;
field=(unsigned char*)MALLOC(val);
if(field==NULL)
{ printk("DMSDOS: simple_check: MDFAT+BITFAT test skipped (no memory)\n");
return 2;
}
for(i=0;i<val;++i)field[i]=0;
for(i=2;i<=dblsb[cvfnr].s_max_cluster2;++i)
{ dbl_mdfat_value(sb,i,cvfnr,NULL,&mde);
if(mde.flags&2)
{
val=mde.sector_minus_1;
if(val+mde.size_lo_minus_1>=dblsb[cvfnr].s_dataend||
val+1<dblsb[cvfnr].s_datastart)
{ printk("DMSDOS: MDFAT entry invalid in CVF %d (cluster %d)\n",
cvfnr+1,i);
FREE(field);
return -2;
}
for(j=0;j<=mde.size_lo_minus_1;++j)
{ ++val;
if(getbit(val))
{ printk("DMSDOS: MDFAT crosslink in CVF %d detected\n",cvfnr+1);
FREE(field);
return -2;
}
setbit(val);
}
}
}
/* check bitfat */
/* dataend-1 problem corrected above - dmsdos doesn't touch the
last sector now because it seems to be reserved... */
for(i=dblsb[cvfnr].s_datastart;i<=dblsb[cvfnr].s_dataend;++i)
{ if(dbl_bitfat_value(sb,i,cvfnr,NULL)!=(getbit(i)?1:0))
{ printk("DMSDOS: BITFAT mismatches MDFAT in CVF %d (sector %d)\n",
cvfnr+1,i);
FREE(field);
return -3;
}
}
FREE(field);
return 0;
}